Angular Services & Dependency Injection

Reusable logic மற்றும் state-ஐ components-இலிருந்து வெளியே organize செய்ய கற்றுக்கொள்ளுங்கள்

Angular Services & Dependency Injection

Services reusable logic மற்றும் state-ஐ components-இலிருந்து வெளியே hold செய்கின்றன.

Services & DI Essentials

  • What: ஒரு service reusable logic/state hold செய்கிறது. DI (dependency injection) தேவைப்படும் இடங்களில் instances supply செய்கிறது.
  • Scope: Shared singleton-க்கு root-ல் provide செய்யவும், அல்லது isolated instances-க்கு component-ல் provide செய்யவும்.
  • Use cases: Data fetching, caching, business rules, cross-component state.
  • Mental model: DI power outlet போன்றது: நீங்கள் plug in செய்கிறீர்கள் மற்றும் Angular உங்களுக்கு ready instance தருகிறது.
  • Decorator: மற்ற services inject செய்யும் classes-ல் @Injectable() பயன்படுத்தவும்.

Service Definition Example

import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class CounterService { value = 0; inc() { this.value++; } }

// Inject in a component
// constructor(public counter: CounterService) {}

Notes

  • Related: Services consume செய்ய Components, services பயன்படுத்தும் Router for guards, மற்றும் data fetch செய்யும் HTTP services ஆகியவற்றை காணவும்.
  • Use @Injectable({ providedIn: 'root' }) for app-wide singletons.
  • Provide at a component when you need isolated instances.
  • Use inject() in functions like route guards to retrieve dependencies outside constructors.

Service Basics

Classes-ஐ @Injectable() உடன் decorate செய்யவும் (அவை மற்ற services inject செய்யும் போது required).

Services-ஐ components-ல் use செய்ய constructors-ல் inject செய்யவும்.

Shared singleton-க்கு providedIn: 'root' பயன்படுத்தவும்.

Service Basics Syntax

@Injectable({ providedIn: 'root' })
export class DataService {}

// class App { constructor(private data: DataService) {} }

Example

Service Basics Example

import { bootstrapApplication } from '@angular/platform-browser';
import { Component, Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class CounterService {
  value = 0;
  inc() { this.value++; }
  dec() { this.value--; }
  reset() { this.value = 0; }
}

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <h3>Services</h3>
    <p>Counter: {{ counter.value }}</p>
    <button (click)="counter.inc()">+</button>
    <button (click)="counter.dec()">-</button>
    <button (click)="counter.reset()">Reset</button>
  `
})
export class App {
  constructor(public counter: CounterService) {}
}

bootstrapApplication(App);

Example Explained

  • @Injectable({ providedIn: 'root' }): App-wide available singleton service register செய்கிறது.
  • Inject in constructor: constructor(public counter: CounterService) service-ஐ template-க்கு expose செய்கிறது.
  • Methods: Shared state update செய்ய inc() / dec() / reset() call செய்யவும்.

Notes

  • Metadata required when injecting: ஒரு service மற்ற services inject செய்யும் போது, DI metadata generate செய்ய @Injectable() add செய்யவும்.
  • Keep state minimal: Services-ல் large shared mutable state avoid செய்யவும்; Explicit methods மற்றும் return values பயன்படுத்தவும்.
  • Standalone bootstrapping: Providers registered ஆக உள்ளதை உறுதிப்படுத்தவும் மற்றும் functional constructs-ல் inject() பயன்படுத்தவும் (e.g., guards).

Shared Service Across Components

providedIn: 'root' app-ல் ஒரு instance share செய்கிறது.

ஒரு component-ல் update செய்வது மற்ற components-ல் reflect ஆகும் அதே service பயன்படுத்தும் போது.

Shared Service Usage

<counter-a></counter-a>
<counter-b></counter-b>
<!-- Both use the same service instance -->

Example

Shared Service Example

import { bootstrapApplication } from '@angular/platform-browser';
import { Component, Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class CounterService {
  value = 0;
  inc() { this.value++; }
  dec() { this.value--; }
}

@Component({
  selector: 'counter-a',
  standalone: true,
  template: `
    <h4>Counter A</h4>
    <p>Value: {{ counter.value }}</p>
    <button (click)="counter.inc()">+1</button>
    <button (click)="counter.dec()">-1</button>
  `
})
export class CounterA {
  constructor(public counter: CounterService) {}
}

@Component({
  selector: 'counter-b',
  standalone: true,
  template: `
    <h4>Counter B</h4>
    <p>Value: {{ counter.value }}</p>
    <button (click)="counter.inc()">+1</button>
    <button (click)="counter.dec()">-1</button>
  `
})
export class CounterB {
  constructor(public counter: CounterService) {}
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CounterA, CounterB],
  template: `
    <h3>Shared Service Across Components</h3>
    <counter-a></counter-a>
    <counter-b></counter-b>
    <p><em>Both components use the same CounterService instance.</em></p>
  `
})
export class App {}

bootstrapApplication(App);

Example Explained

  • providedIn: 'root': App-ல் ஒரு instance share செய்கிறது.
  • Shared state: ஒரு component-ல் +/− click செய்வது மற்ற component-ல் காட்டப்படும் value-ஐ update செய்கிறது.
  • Template access: Public ஆக injected ஆகிறது அதனால் template counter.value-ஐ read செய்ய முடியும் மற்றும் methods call செய்ய முடியும்.

Notes

  • Singleton vs local: providedIn: 'root' ஒரு shared instance yield செய்கிறது; component-ல் providing per subtree separate instances create செய்கிறது.
  • Be deliberate about scope: உங்கள் sharing needs match ஆகும் level-ல் provide செய்யவும் surprises avoid செய்ய.

Component-Provided Service (Hierarchical DI)

அதன் subtree-க்கு local instance create செய்ய component-ன் providers-ல் service provide செய்யவும்.

Sibling subtrees separate instances receive செய்கின்றன.

Component-Provided Service Syntax

@Component({ providers: [LocalCounterService] })
export class Panel {}

Example

Component-Provided Service Example

import { bootstrapApplication } from '@angular/platform-browser';
import { Component, Injectable } from '@angular/core';
import { CommonModule } from '@angular/common';

@Injectable()
export class LocalCounterService {
  id = Math.floor(Math.random() * 10000);
  value = 0;
  inc() { this.value++; }
}

@Component({
  selector: 'counter-view',
  standalone: true,
  template: `
    <p>Service #{{ svc.id }} value: {{ svc.value }}</p>
    <button (click)="svc.inc()">+1</button>
  `
})
export class CounterView {
  constructor(public svc: LocalCounterService) {}
}

@Component({
  selector: 'panel-a',
  standalone: true,
  imports: [CommonModule, CounterView],
  providers: [LocalCounterService],
  template: `
    <h4>Panel A (own provider)</h4>
    <counter-view></counter-view>
    <counter-view></counter-view>
  `
})
export class PanelA {}

@Component({
  selector: 'panel-b',
  standalone: true,
  imports: [CommonModule, CounterView],
  providers: [LocalCounterService],
  template: `
    <h4>Panel B (own provider)</h4>
    <counter-view></counter-view>
    <counter-view></counter-view>
  `
})
export class PanelB {}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, PanelA, PanelB],
  styles: [`
    .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
    h4 { margin: 0 0 8px; }
    button { margin-top: 6px; }
  `],
  template: `
    <h3>Component-Provided Service (Hierarchical DI)</h3>
    <p>Each panel provides its own service instance.</p>
    <p>Counters inside the same panel share the instance.</p>
    <p>Different panels do not.</p>
    <div class="grid">
      <panel-a></panel-a>
      <panel-b></panel-b>
    </div>
  `
})
export class App {}

bootstrapApplication(App);

Example Explained

  • providers: [LocalCounterService]: ஒவ்வொரு panel component அதன் subtree-க்கு அதன் சொந்த service instance பெறுகிறது.
  • Within a panel: பல CounterView children அதே local instance share செய்கின்றன.
  • Between panels: Panel A மற்றும் Panel B வெவ்வேறு instances கொண்டுள்ளன (வெவ்வேறு id values காணவும்).

Notes

  • Multiple instances expected: ஒவ்வொரு provided component subtree அதன் சொந்த instance பெறுகிறது; siblings அதை share செய்யாது.
  • Avoid circular dependencies: இரண்டு services ஒருவருக்கொருவர் inject செய்வது fail ஆகும்; common logic-ஐ third service-ல் extract செய்யவும் அல்லது redesign செய்யவும்.

Service Design Tips

Services focused மற்றும் easy to test-காக வைக்கவும்:

Avoid component coupling

Services-ல் components inject செய்யாதீர்கள்; services UI-agnostic-காக வைக்கவும்.

Expose clear APIs

Small methods plain values அல்லது Observables return செய்ய பயன்படுத்தவும்; internal state private-காக வைக்கவும்.

Configuration via tokens

Configurable values-க்கு injection tokens பயன்படுத்தவும் (unique lookup key, often a class or an InjectionToken object) testing மற்றும் reuse simplify செய்ய.

Scope deliberately

App-wide singletons-க்கு providedIn: 'root' பயன்படுத்தவும்; isolated instances-க்கு component-ல் provide செய்யவும்.

Exercise

Which decorator marks a class for Angular's dependency injection?

@Injectable
✓ Correct! @Injectable decorator ஒரு class-ஐ Angular's dependency injection-க்கு mark செய்கிறது
@Component
✗ Incorrect! @Component decorator components-க்கு பயன்படுகிறது, dependency injection-க்கு அல்ல
@Input
✗ Incorrect! @Input decorator component properties-க்கு பயன்படுகிறது, dependency injection-க்கு அல்ல
அடுத்தது